package trading.algo.BullTask;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import trading.algo.MasterAlgo;
import trading.algo.OpenOrdersTask;
import trading.algo.TimerKillerTask;
import trading.algo.UpdateAccountValueTask;
import trading.bo.DBAccessorBO;
import trading.bo.EmailAccessorBO;
import trading.bo.IBAccessorBO;
import trading.dataentry.MonitoredStocks;
import trading.util.Constants;
import trading.util.MarketTimeHelper;
import trading.util.OrderSplitHelper;

public class MarketHourBullTask extends TimerTask {
    private Long lastNewOrderTimestamp = null;
    private Long newOrderWaitTime;

    private Double historicalLow = null;
    private Double yesterdayHistoricalLow = null;

    private DBAccessorBO dbAccessorBO;
    private IBAccessorBO ibAccessorBO;
    private MasterAlgo masterAlgo;
    private EmailAccessorBO emailAccessorBO;
    private OrderSplitHelper orderSplitHelper;

    public MarketHourBullTask(IBAccessorBO ibAccessorBO,
            DBAccessorBO dbAccessorBO, MasterAlgo masterAlgo,
            MarketTimeHelper marketTimeHelper, EmailAccessorBO emailAccessorBO,
            OrderSplitHelper orderSplitHelper) {
        this.ibAccessorBO = ibAccessorBO;
        this.dbAccessorBO = dbAccessorBO;
        this.masterAlgo = masterAlgo;
        this.emailAccessorBO = emailAccessorBO;
        this.orderSplitHelper = orderSplitHelper;
        newOrderWaitTime = (long) Constants.Algo.BULL_NEW_ORDER_WAIT_TIME;
    }

    @Override
    public void run() {
        // Step 1: check if market turns to bear
        MonitoredStocks stockStat = this.dbAccessorBO
                .getMonitoredStockData(MonitoredStocks.TICKERS.UVXY.name());
        if (yesterdayHistoricalLow == null) {
            yesterdayHistoricalLow = stockStat.getHistoricalLow();
        }
        if (historicalLow == null) {
            historicalLow = yesterdayHistoricalLow;
        }
        double lastPrice = stockStat.getLastPrice();
        double percentDiff = (lastPrice - historicalLow) * 100.0
                / historicalLow;
        if (percentDiff >= Constants.Algo.BULL_TO_BEAR_PERCENT_CHANGE_CRITERIA) {
            this.masterAlgo.fromBullToBullToBear();
            return;
        }
        // Step 2: check if last price is lower than historical low, if so,
        // create new short orders
        Timer timer = new Timer();
        int taskID = 0;
        if (lastPrice < historicalLow) {
            historicalLow = lastPrice;
            Date currentTime = new Date();
            if (this.lastNewOrderTimestamp == null
                    || currentTime.getTime() - lastNewOrderTimestamp > newOrderWaitTime) {
                this.lastNewOrderTimestamp = currentTime.getTime();
                UpdateAccountValueTask updateAccountValueTask = new UpdateAccountValueTask(
                        ibAccessorBO, dbAccessorBO, false);
                timer.schedule(updateAccountValueTask, taskID * 1000);
                taskID += UpdateAccountValueTask.secondsNeeded();

                double trailAmount = historicalLow
                        * Constants.Algo.BULL_SHORT_PROTECTION_PERCENTAGE
                        / 100.0;
                NewSTKShortOrdersTask newSTKShortOrderTask = new NewSTKShortOrdersTask(
                        ibAccessorBO, dbAccessorBO, emailAccessorBO,
                        trailAmount, historicalLow + trailAmount,
                        orderSplitHelper);
                timer.schedule(newSTKShortOrderTask, taskID * 1000);
                taskID += 25;
            }
            this.emailAccessorBO.sendNewHistoricalLowEmail(
                    MonitoredStocks.TICKERS.UVXY.name(), historicalLow + "");
        }

        // Step 3: check if historical low is much lower than yesterday's
        // historical low, if so, we have to adjust trail amount for existing
        // trail orders
        if (this.shouldUpdateTrailAmount(yesterdayHistoricalLow)) {
            Date currentTime = new Date();
            this.lastNewOrderTimestamp = currentTime.getTime();
            this.newOrderWaitTime = (long) Constants.Algo.BULL_NEW_ORDER_WAIT_TIME;

            // sync open orders
            timer.schedule(new OpenOrdersTask(ibAccessorBO, dbAccessorBO),
                    taskID * 1000);
            taskID += 5;

            timer.schedule(new UpdateTrailOrderMasterTask(ibAccessorBO,
                    dbAccessorBO, emailAccessorBO, this.historicalLow),
                    taskID * 1000);
            taskID += 25;

            // Update yesterday historical low so we don't need to unnecessarily
            // update trail amount all the time.
            this.yesterdayHistoricalLow = this.historicalLow;
        }

        TimerKillerTask timerKillerTask = new TimerKillerTask(timer);
        timer.schedule(timerKillerTask, taskID * 1000);

    }

    private boolean shouldUpdateTrailAmount(double yesterdayHistoricalLow) {
        double trailAmount = yesterdayHistoricalLow
                * Constants.Algo.BULL_SHORT_PROTECTION_PERCENTAGE / 100.0;
        double mktChangeCriteria = this.historicalLow
                * Constants.Algo.BULL_TO_BEAR_PERCENT_CHANGE_CRITERIA / 100.0;
        return trailAmount > mktChangeCriteria;
    }
}
